查看原文
其他

.NET 6 中的 ConfigurationManager

DotNet 2022-07-19

The following article is from amazingdotnet Author WeihanLi

介绍


.NET 6 为了 Minimal API 引入了一些新东西,其中就包含了一个全新的配置对ConfigurationManager这并不是 .NET Framework 里的静态类 ConfigurationManager,而是 .NET Core 里的配置 Microsoft.Extensions.Configuration 中的一个新类型,新的 ConfigurationManager 对象是之前的 ConfigurationBuilderConfigurationRoot 的结合


Implement


API Proposal:

namespace Microsoft.Extensions.Configuration
{
    public sealed class ConfigurationManager : IConfigurationRootIConfigurationBuilderIDisposable
    {
        public ConfigurationManager();
        public stringthis[string key] { getset; }
        public IConfigurationSection GetSection(string key);
        public void Dispose();
    }
}

Implement:

public sealed class ConfigurationManager : IConfigurationBuilderIConfigurationRootIDisposable
{
    private readonly ConfigurationSources _sources;
    private readonly ConfigurationBuilderProperties _properties;
    private readonly object _providerLock = new();
    private readonly List<IConfigurationProvider> _providers = new();
    private readonly List<IDisposable> _changeTokenRegistrations = new();
    private ConfigurationReloadToken _changeToken = new();

    /// <summary>
    /// Creates an empty mutable configuration object that is both an <see cref="IConfigurationBuilder"/> and an <see cref="IConfigurationRoot"/>.
    /// </summary>
    public ConfigurationManager()
    {
        _sources = new ConfigurationSources(this);
        _properties = new ConfigurationBuilderProperties(this);
        
        // Make sure there's some default storage since there are no default providers.
        this.AddInMemoryCollection();
        AddSource(_sources[0]);
    }
    // ...
}

ConfigurationManager在添加 ConfigurationSource 的时候也会注册 IConfigurationProvider,这样在添加 Source 之后就能够拿到 Configuration 中的配置了,在实现上,微软封装了一个私有的 ConfigurationSource 的类型,这里我们看一下注册配置源的代码

private class ConfigurationSources : IList<IConfigurationSource>
{
    private readonly List<IConfigurationSource> _sources = new();
    private readonly ConfigurationManager _config;

    public ConfigurationSources(ConfigurationManager config)
    {
        _config = config;
    }

    public IConfigurationSource this[int index]
    {
        get => _sources[index];
        set
        {
            _sources[index] = value;
            _config.ReloadSources();
        }
    }

    public void Add(IConfigurationSource source)
    {
        _sources.Add(source);
        _config.AddSource(source);
    }
    // ...
}

ConfigurationManager 中的 AddSource 方法实现如下:

IConfigurationBuilder IConfigurationBuilder.Add(IConfigurationSource source)
{
    _sources.Add(source ?? throw new ArgumentNullException(nameof(source)));
    return this;
}

private void RaiseChanged()
{
    var previousToken = Interlocked.Exchange(ref _changeToken, new ConfigurationReloadToken());
    previousToken.OnReload();
}

// Don't rebuild and reload all providers in the common case when a source is simply added to the IList.
private void AddSource(IConfigurationSource source)
{
    lock (_providerLock)
    {
        var provider = source.Build(this);
        _providers.Add(provider);

        provider.Load();
        _changeTokenRegistrations.Add(ChangeToken.OnChange(() => provider.GetReloadToken(), () => RaiseChanged()));
    }

    RaiseChanged();
}

可以看到每次新加一个配置源的时候,都会去构建对应的一个 IConfigurationProvider 而且会去加载配置数据并注册配置更新事件,所以我们注册完配置之后才能够获取到配置,更多实现细节参考 Github 上的源码:https://github.com/dotnet/runtime/blob/v6.0.0-rc.1.21451.13/src/libraries/Microsoft.Extensions.Configuration/src/ConfigurationManager.cs

Sample

来看下面使用时的 Sample 吧,非常的简单

const string testKey = "test";

var configuration = new ConfigurationManager();
Console.WriteLine(configuration[testKey]);

configuration.AddInMemoryCollection(new Dictionary<stringstring>()
{
    { testKey, "test" }
});
Console.WriteLine(configuration[testKey]);
Console.ReadLine();

输出结果如下:第一次输出的时候还没有注册配置输出的是空,第一次输出的时候已经注册了配置输出的是我们配置的值代码示例在可以从 Github 获取 https://github.com/WeihanLi/SamplesInPractice/blob/master/net6sample/ConfigurationManagerSample/Program.cs

总结

目前来说,ConfigurationManager 对象主要是为了 .NET 6 的 Minimal API 的需要,.NET 6 的 Minimal API 里用了这个,可以参考:https://github.com/dotnet/aspnetcore/blob/v6.0.0-rc.1.21452.15/src/DefaultBuilder/src/WebApplicationBuilder.cs ,但就像上面的示例一样,我们也是可以直接使用的,而且原来的 IConfigurationBuilder 依然是可以用的,无需担心升级到 .NET 6 会 break 的问题。

对于需要用到配置的测试程序直接用 ConfigurationManager 会更为简单一些,不需要先声明一个 ConfigurationBuilder 的对象注册好配置之后再构建一个 IConfiguration 对象,直接用一个对象就可以了,至少从我们写代码的角度会简单很多,但是性能会稍差一些,注册的配置源越多越明显,因为 ConfigurationManager 每次注册配置源的时候都会去构建和注册 IConfigurationProvider  而 IConfigurationBuilder 则是在最后 Build 的时候才构建一次,不过通常我们的配置也只是启动时只用配置一次,个人认为是可以接受的

- EOF -

推荐阅读  点击标题可跳转
.NET 某纺织工厂 MES系统 API 挂死分析 C# 中多线程间的同步.NET Core 利用反射动态加载类库的方法


看完本文有收获?请转发分享给更多人

推荐关注「DotNet」,提升.Net技能 

点赞和在看就是最大的支持❤️

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存